package com.xian.im.message;


import com.xian.im.common.enums.ConsumerQueueEnum;
import com.xian.im.common.utils.bussiness.GroupContextUtil;
import com.xian.im.message.queue.MessageGroupQueue;
import com.zaxxer.hikari.util.DriverDataSource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.sql.*;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;


/**
 * @program:
 * @description:
 * @author: liru.xian
 * @create: 2020-12-08 13:25
 **/
@Slf4j
@Configuration
public class RabbitAutoBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware,EnvironmentAware {

    private static BeanDefinitionRegistry beanDefinitionRegistry;
    private static ApplicationContext applicationContext;
    private static Environment env;
    public static final String BIND_SUFFIX = ":bind";
    public static final String EX_SUFFIX = ":ex";


    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        RabbitAutoBeanDefinitionRegistryPostProcessor.beanDefinitionRegistry = registry;
        Set<String> allGroupAccount =getAccount();
        MessageGroupQueue.init(allGroupAccount);
        GroupContextUtil.init(allGroupAccount);
        for (String s : allGroupAccount) {
            log.info("声明群组消息队列：{}",s);
            Queue queue = new Queue(s+id(),true,false,true);
            beanDefinitionRegistry.registerBeanDefinition( s, BeanDefinitionBuilder.genericBeanDefinition( Queue.class, () -> { return queue; } ).getBeanDefinition());
            Exchange exchange = new FanoutExchange(s,true,false);
            beanDefinitionRegistry.registerBeanDefinition( s+EX_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition(Exchange.class, () -> exchange).getBeanDefinition() );
            Binding binding = new Binding(queue.getName(), Binding.DestinationType.QUEUE, s, "", null);
            beanDefinitionRegistry.registerBeanDefinition( s+BIND_SUFFIX, BeanDefinitionBuilder.genericBeanDefinition( Binding.class, () -> binding ).getBeanDefinition() );
        }

        declareQueue();
        declareExchange();
        declareQueueAndBinding();
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        RabbitAutoBeanDefinitionRegistryPostProcessor.applicationContext = applicationContext;
    }

    @Override
    public void setEnvironment(Environment environment) {
        RabbitAutoBeanDefinitionRegistryPostProcessor.env = environment;
    }

    public static <T>T getBean(String bean){
        return (T) applicationContext.getBean(bean);
    }

    public static <T>T getBean(Class bean){
        return (T) applicationContext.getBean(bean);
    }

    /**
     * 声明队列 拼装
     */
    void declareQueue(){
        for (ConsumerQueueEnum anEnum : ConsumerQueueEnum.values()) {
            if(anEnum.getEffect()){
                beanDefinitionRegistry.registerBeanDefinition( anEnum.getFactoryName(), BeanDefinitionBuilder.genericBeanDefinition( Queue.class, () -> {
                    return new Queue(anEnum.getFactoryName()+id(),true,false,true,null);
                } ).getBeanDefinition());
            }
        }
    }

    /**
     * 声明交换机 拼装
     */
    void declareExchange(){
        for (ConsumerQueueEnum anEnum : ConsumerQueueEnum.values()) {
            if(anEnum.getEffect()){
                beanDefinitionRegistry.registerBeanDefinition( anEnum.getExchange(),
                        BeanDefinitionBuilder.genericBeanDefinition(
                                Exchange.class, () -> new ExchangeBuilder(anEnum.getExchange(), anEnum.getExchangeTypes())
                                        .durable( true ).build()).getBeanDefinition() );
            }
        }
    }
    /**
     * 绑定 交换机和队列
     */
    void declareQueueAndBinding(){
        for (ConsumerQueueEnum anEnum : ConsumerQueueEnum.values()) {
            if(anEnum.getEffect()){
                Binding binding = BindingBuilder.bind(  (Queue)getBean(anEnum.getFactoryName()) ).to( (Exchange)getBean(anEnum.getExchange()) ).with( anEnum.getFactoryName() ).noargs();;
                beanDefinitionRegistry.registerBeanDefinition( anEnum.getFactoryName()+BIND_SUFFIX,
                        BeanDefinitionBuilder.genericBeanDefinition( Binding.class, () -> binding ).getBeanDefinition() );
            }
        }
    }

    Set<String> getAccount() throws SQLException {

        String driverClassName = env.getProperty("spring.datasource.driverClassName");
        String jdbcUrl = env.getProperty("spring.datasource.url");
        String username =env.getProperty("spring.datasource.username");
        String password =env.getProperty("spring.datasource.password");
        Connection connection = null;
        DataSource dataSource = null;
        Statement statement = null;
        try {
            dataSource = new DriverDataSource(jdbcUrl,driverClassName,new  Properties(),username,password);
            connection = dataSource.getConnection();
            statement = connection.createStatement();
            ResultSet query = statement.executeQuery("SELECT group_unique_id FROM xian_im_group WHERE delete_flag=0");
            Set<String> list = new HashSet<>();
            //获取键名
            ResultSetMetaData md = query.getMetaData();
            //获取行的数量
            int columnCount = md.getColumnCount();
            while (query.next()) {
                for (int i = 1; i <= columnCount; i++) {
                    list.add(query.getObject(i).toString());
                }
            }
            return list;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
            if (dataSource != null) {
                dataSource = null;
            }
        }
        return new HashSet<>();
    }


    public static String id(){
        String port = env.getProperty("xim.server.port");
        String host = env.getProperty("xim.server.host");
        return "_"+host+":"+port;
    }

}
